/*
 *
 *
 *
 *
 */


package to.pm.magazzino.ejb;

import java.rmi.RemoteException;
import java.util.Date;
import java.util.LinkedList;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.annotation.Resource;
import javax.ejb.EJBException;
import javax.transaction.SystemException;
import to.pm.entity.magazzino.DettaglioRicezione;
import to.pm.entity.magazzino.Ricezione;
import to.pm.magazzino.bi.RicezioneBusiness;
import javax.ejb.Stateful;
import javax.ejb.TransactionManagement;
import javax.ejb.TransactionManagementType;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.PersistenceContextType;
import javax.transaction.UserTransaction;
import to.pm.entity.acquisti.DOECategoria;
import to.pm.entity.acquisti.DOEProdotto;
import to.pm.entity.acquisti.DettaglioOrdineEsterno;
import to.pm.entity.acquisti.OrdineEsterno;
import to.pm.entity.catalogo.Prodotto;
import to.pm.entity.catalogo.ProdottoConsumabile;
import to.pm.entity.catalogo.ProdottoInventariabile;
import to.pm.entity.magazzino.DettaglioRicezioneConsumabile;
import to.pm.entity.magazzino.DettaglioRicezioneInventariabile;
import to.pm.entity.magazzino.DisponibilitaProdotto;
import to.pm.entity.magazzino.ProdottoInventariato;
import to.pm.entity.magazzino.Stock;


@Stateful
//@TransactionManagement(value=TransactionManagementType.CONTAINER)
@TransactionManagement(value=TransactionManagementType.BEAN)
public class RicezioneBean implements RicezioneBusiness/*, SessionSynchronization*/{

    Logger log = Logger.getAnonymousLogger();

    @PersistenceContext(type = PersistenceContextType.EXTENDED)
    private EntityManager em;

    @Resource
    //private EJBContext ctx;
    private UserTransaction utx;

    private Ricezione ricezione ;
    private OrdineEsterno ordineEsterno;



    //@TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
    public void iniziaRicezione(OrdineEsterno ordine) {
        log.info("iniziaRicezione:"+ordine);
        ordineEsterno = em.getReference(OrdineEsterno.class, ordine.getId());
        if (ordineEsterno.getStato().isInRicezione()) {
           ricezione = new Ricezione();
           ricezione.setDataRicezione(new Date());
           ricezione.setOrdine(ordineEsterno);

           try {
                utx.begin();
                utx.setTransactionTimeout(600);
            } catch (Exception ex) {
                Logger.getLogger(RicezioneBean.class.getName()).log(Level.SEVERE, null, ex);
                throw new RuntimeException(ex);
            }

           em.persist(ricezione);
           List<Ricezione> lr = ordineEsterno.getRicezioniOrdine();
           lr.add(ricezione);
           ordineEsterno.setRicezioniOrdine(lr);
           em.merge(ordineEsterno);
           
        } else {
            throw (new RuntimeException("Ordine non in ricezione"));
        }
    }


    //@TransactionAttribute(TransactionAttributeType.MANDATORY)
    public void annullaRicezione() {
        try {
                log.info("annullaRicezione: setRollbackOnly();");
                utx.rollback();
            } catch (Exception ex) {
                Logger.getLogger(RicezioneBean.class.getName()).log(Level.SEVERE, null, ex);
                throw new RuntimeException(ex);
            }
        ricezione = null;
    }


    //@TransactionAttribute(TransactionAttributeType.MANDATORY)
    public void riceviDettaglioOrdineProdotto(DOEProdotto doep, DettaglioRicezione dr) {
        //precondizione: in DettaglioRicezione c'e' gia' la quantita
        log.info("riceviDettaglioOrdineProdotto");
         try {
            log.info("stato transizione: " + utx.getStatus());
        } catch (SystemException ex) {
            Logger.getLogger(RicezioneBean.class.getName()).log(Level.SEVERE, null, ex);
        }
            doep = em.getReference(DOEProdotto.class, doep.getId()) ;


            if (!doep.getStato().isInRicezione()){
                throw new RuntimeException("DOE non e in stato di ricezione...");
            }

           if (doep.getProdotto().isConsumabile() && dr.isConsumabile()) {

               riceviConsumabile(doep,
                                 (ProdottoConsumabile)doep.getProdotto(),
                                 (DettaglioRicezioneConsumabile) dr);

           } else if (doep.getProdotto().isInventariabile() && dr.isInventariabile()) {

               riceviInventariabile(doep,
                                    (ProdottoInventariabile)doep.getProdotto(),
                                    (DettaglioRicezioneInventariabile) dr);

           } else {
               throw (new RuntimeException ("Errore di compatibilita tra ricezione e ordine") );
           }

    }

    //@TransactionAttribute(TransactionAttributeType.MANDATORY)
    public void riceviDettaglioOrdineCategoria(DOECategoria doec, Prodotto prodotto, DettaglioRicezione dr){
            doec = em.getReference(doec.getClass(), doec.getId()) ;

            if (!doec.getStato().isInRicezione()){
                throw new RuntimeException("DOE non e in stato di ricezione...");
            }

           if (doec.getCategoria().isConsumabile() && dr.isConsumabile() && prodotto.isConsumabile()) {

               riceviConsumabile(doec,
                                 (ProdottoConsumabile)prodotto,
                                 (DettaglioRicezioneConsumabile) dr);

           } else if (doec.getCategoria().isInventariabile() && dr.isInventariabile() && prodotto.isInventariabile()) {

               riceviInventariabile(doec,
                                    (ProdottoInventariabile)prodotto,
                                    (DettaglioRicezioneInventariabile) dr);

           } else {
               throw (new RuntimeException ("Errore di compatibilita tra categoria ordine, ricezione e prodotto") );
           }

    }

    private void riceviInventariabile(DettaglioOrdineEsterno doe, ProdottoInventariabile prodotto, DettaglioRicezioneInventariabile dr){

        log.info("Ricevo prodotto inventariabile (ricezione inventariabili da definire...)");

        List<DettaglioRicezione> ldr = doe.getDettaglioRicezioni();
        ldr.add(dr);
        doe.setDettaglioRicezioni(ldr);
        dr.setRicezioneOrdine(ricezione);
        dr.setDettaglioOrdine(doe);

        em.persist(dr);
        em.persist(doe);

        ProdottoInventariato pi = new ProdottoInventariato();
        pi.setProdotto(prodotto);
        pi.setDettaglioRicezione(dr);
        pi.setCodiceInventario(null);

        em.persist(pi);
    }

    private void riceviConsumabile(DettaglioOrdineEsterno doe, ProdottoConsumabile prodotto, DettaglioRicezioneConsumabile dr){
        //precondizione: verifiche fatte gia' prima
        //post condizione: stock con disponibilita prodotto in DettaglioRicezioneConsumabile
        log.info("Ricevo prodotto consumabile");
        try {
            log.info("stato transizione: " + utx.getStatus());
        } catch (SystemException ex) {
            Logger.getLogger(RicezioneBean.class.getName()).log(Level.SEVERE, null, ex);
        }

        List<DettaglioRicezione> ldr = doe.getDettaglioRicezioni();
        ldr.add(dr);
        doe.setDettaglioRicezioni(ldr);
        dr.setRicezioneOrdine(ricezione);


        Stock stock = aggiornaStock(doe, prodotto, dr);
        dr.setStock(stock);

        dr.setDettaglioOrdine(doe);

        em.persist(dr);

    }


    private Stock aggiornaStock(DettaglioOrdineEsterno doe, ProdottoConsumabile p, DettaglioRicezioneConsumabile drNew) {
         Stock stock = null;
         if (doe.getDettaglioRicezioni() != null && !doe.getDettaglioRicezioni().isEmpty()) {
             DettaglioRicezioneConsumabile drOld = (DettaglioRicezioneConsumabile)doe.getDettaglioRicezioni().iterator().next();
             stock = drOld.getStock();
             stock.setQuantitaIniziale(stock.getQuantitaIniziale()+drNew.getQuantita());
             

         } else {
             stock = new Stock();

             stock.setDettaglioRicezioni(new LinkedList<DettaglioRicezioneConsumabile>());
             stock.getDettaglioRicezioni().add(drNew);
             stock.setDisponibilita(new LinkedList<DisponibilitaProdotto>());
             stock.setProdotto(p);

             stock.setQuantitaIniziale(drNew.getQuantita());
             em.persist(stock);
         }

        DisponibilitaProdotto dp = new DisponibilitaProdotto();
        dp.setStock(stock);
        dp.setCollocazione(null);
        dp.setQuantita(drNew.getQuantita());
        em.persist(dp);
        stock.getDisponibilita().add(dp);
        em.merge(stock);
        return stock;
    }


    public Ricezione confermaRicezione() {
        log.info("conferma ricezione");
        try {
            log.info("stato transizione: "+utx.getStatus());
            em.flush();
            //em.close();
            utx.commit();
            log.info("stato transizione: "+utx.getStatus());
            log.info("commitata ricezione");
        } catch (Exception ex) {
            Logger.getLogger(RicezioneBean.class.getName()).log(Level.SEVERE, null, ex);
            throw new RuntimeException(ex);
        }
        return ricezione;
    }

    public void afterBegin() throws EJBException, RemoteException {
        log.info("afterBegin: Not supported yet.");
    }

    public void beforeCompletion() throws EJBException, RemoteException {
        log.info("beforeCompletion: Not supported yet.");
    }

    public void afterCompletion(boolean committed) throws EJBException, RemoteException {
        log.info("afterCompletion("+committed+"): Not supported yet.");
    }

    public void riceviDettaglio(DettaglioOrdineEsterno doe, Prodotto p, int quantita) {
        if (doe.getIsCategoria()) {

            DOECategoria doec = (DOECategoria) doe;
            if (p == null) {
                throw (new RuntimeException("Ricezione dettaglio categoria senza aver specificato un prodotto!"));
            }

            DettaglioRicezione dr;
            if (p.isConsumabile()) {
                dr = new DettaglioRicezioneConsumabile();
            } else {
                dr = new DettaglioRicezioneInventariabile();
            }

            dr.setQuantita(quantita);
            riceviDettaglioOrdineCategoria(doec, p, dr);

        } else {

            DOEProdotto doep = (DOEProdotto) doe;

            p = em.getReference(Prodotto.class, doep.getProdotto().getId());

            DettaglioRicezione dr;
            if (p.isConsumabile()) {
                dr = new DettaglioRicezioneConsumabile();
            } else {
                dr = new DettaglioRicezioneInventariabile();
            }

            dr.setQuantita(quantita);
            riceviDettaglioOrdineProdotto(doep, dr);
        }
    }

   
   
}
